// proxy除了可以代理对象, 还能代理函数, 其实函数也是对象
let activeEffect
let effectStack = []
const data = {
  name: 'jack',
  foo: 90,
  get bar() {
    return this.foo
  }
}
const bucket = new WeakMap()
const obj = new Proxy(data, {
  get(target, key, receiver) {
    track(target, key)
    return Reflect.get(target, key, receiver)
  },
  set(target, key, value) {
    target[key] = value
    trigger(target, key)
  }
})

function track(target, key) {
  let depMaps = bucket.get(target)
  if (!depMaps) {
    bucket.set(target, (depMaps = new Map()))
  }
  let deps = depMaps.get(key)
  if (!deps) {
    depMaps.set(key, (deps = new Set()))
  }
  if (activeEffect) {
    deps.add(activeEffect)
    activeEffect.deps.push(deps)
  }
}

function trigger(target, key) {
  const depMaps = bucket.get(target)
  const deps = depMaps.get(key)
  const runDeps = new Set(deps)
  runDeps.forEach((effect) => {
    if (effect.options.schedule) {
      effect.options.schedule(effect)
    } else {
      effect()
    }
  })
}

function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}
const effect = (fn, options = {}) => {
  const effectFn = () => {
    activeEffect = effectFn
    effectStack.push(effectFn)
    cleanup(effectFn)
    const res = fn()
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
    return res
  }
  effectFn.deps = []
  effectFn.options = options
  if (options.lazy) {
    return effectFn
  } else {
    effectFn()
  }
}

effect(
  () => {
    console.log(obj.name, '触发')
  },
  {
    schedule() {
      console.log(111)
    }
  }
)

const computed = (cb) => {
  let dirty = true
  let value = ''
  const obj = {
    get value() {
      track(obj, 'value')
      if (dirty) {
        value = effectFn()
        dirty = false
        return value
      } else {
        return value
      }
    }
  }
  const effectFn = effect(() => cb(), {
    lazy: true,
    schedule() {
      if (!dirty) {
        dirty = true
        trigger(obj, 'value')
      }
    }
  })
  return obj
}

function fn(name) {
  console.log(name, 'this is fn')
}
const proxyFn = new Proxy(fn, {
  apply(target, thisArgs, argArray) {
    target.call(thisArgs, ...argArray)
  }
})
// 代理调用
proxyFn('123')
// 以上被称为: ‘基本操作’
// 而调用对象下的方法, 如: obj.fn就是非基本操作, 也可以说是符合操作
// Reflect
const obj2 = { name: 'jack' }
console.log(Reflect.get(obj2, 'name'), 'name')

const info = {
  foo: 12,
  get bar() {
    return this.foo
  }
}

console.log(info.bar)
// 12

// 通过Reflect的第三个参数可以改变访问器属性bar中的this!
// 下面就返回了13
console.log(Reflect.get(info, 'bar', { foo: 13 }))
// 13

effect(() => {
  console.log(obj.bar, '改变了嘛?')
})

obj.foo = 19
obj.foo = 20

/**
 * 解析:
 * 如果不使用Reflect.get(obj, key, receiver), 那么, 访问器属性bar的函数中的this, 指向的将是原始对象data, 而不是代理对象obj!
 * 所以要通过receiver, 来告知, this应该指向代理对象!
 */
